P11-2. Grafana 상관분석과 Kiali 토폴로지 - 실습 검증

P11-2. Grafana 상관분석과 Kiali 토폴로지 - 실습 검증

실습 목표

  1. monitoring 네임스페이스에 **Grafana (v11.5.2)**를 배포하며, 코드형 선언적 프로비저닝(datasources.yaml)을 통해 VictoriaMetrics(Metrics) · Loki(Logs) · Tempo(Traces) 3대 데이터소스와 **상관분석 점프 규칙(tracesToLogsV2, derivedFields, exemplarTraceIdDestinations)**을 한 번에 자동 구성한다.
  2. istio-system 네임스페이스에 **Kiali Server (v2.33.0)**를 Helm으로 배포하고, 외부 VictoriaMetrics·Grafana·Tempo 엔드포인트와 연동하여 Ambient Mesh(ztunnel + waypoint) 및 외부 PostgreSQL 18 VM(pg-vm.mesh.internal)까지의 실시간 서비스 토폴로지 그래프 JSON과 **Istio CRD 정합성 검증 상태(WaypointBound)**를 실측한다.
  3. kubectl port-forward를 통해 로컬 브라우저에서 Grafana Explore 화면과 Kiali 대시보드에 접속하여 Metrics → Traces → Logs 3단계 상관분석 워크플로우를 검증한다.


1. 사전 환경 점검

Part 08~10에서 구축한 monitoring 네임스페이스의 수집기(alloy) 및 3대 백엔드 스토리지(vmsingle, loki, tempo)가 모두 정상 동작 중인지 확인한다.

kubectl get pods,svc -n monitoring
NAME                            READY   STATUS    RESTARTS   AGE
pod/alloy-659d8d98d4-68896      1/1     Running   0          10m
pod/loki-77f5c85fd7-fssmd       1/1     Running   0          10m
pod/tempo-7b97b8b6f4-5gbcx      1/1     Running   0          10m
pod/vmsingle-7b77cc877c-5ds97   1/1     Running   0          16m

NAME               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
service/alloy      ClusterIP   10.96.182.114   <none>        12345/TCP,4317/TCP,4318/TCP  10m
service/loki       ClusterIP   10.96.105.192   <none>        3100/TCP                     10m
service/tempo      ClusterIP   10.96.239.141   <none>        3200/TCP,4317/TCP,4318/TCP   10m
service/vmsingle   ClusterIP   10.96.135.209   <none>        8428/TCP                     16m

2. Grafana 11.5 배포 및 3대 데이터소스 상관분석 자동 프로비저닝

2.1 매니페스트 작성 ($HOME\mesh-study\part11-grafana.yaml)

웹 UI에서 수동으로 데이터소스를 클릭해 등록하는 대신, /etc/grafana/provisioning/datasources/datasources.yaml ConfigMap을 마운트하여 컨테이너 기동 즉시 VictoriaMetrics · Loki · Tempo와 상호 참조 링크(uid: victoriametrics, uid: loki, uid: tempo)가 자동 구성되도록 작성한다.

@'
apiVersion: v1
kind: ConfigMap
metadata:
  name: grafana-datasources
  namespace: monitoring
data:
  datasources.yaml: |
    apiVersion: 1
    datasources:
      - name: VictoriaMetrics
        type: prometheus
        uid: victoriametrics
        access: proxy
        url: http://vmsingle.monitoring.svc.cluster.local:8428
        isDefault: true
        jsonData:
          httpMethod: POST
          exemplarTraceIdDestinations:
            - name: trace_id
              datasourceUid: tempo
      - name: Loki
        type: loki
        uid: loki
        access: proxy
        url: http://loki.monitoring.svc.cluster.local:3100
        jsonData:
          derivedFields:
            - datasourceUid: tempo
              matcherRegex: '"([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})"'
              name: RequestID
              url: '${__value.raw}'
      - name: Tempo
        type: tempo
        uid: tempo
        access: proxy
        url: http://tempo.monitoring.svc.cluster.local:3200
        jsonData:
          tracesToLogsV2:
            datasourceUid: loki
            spanStartTimeShift: '-1m'
            spanEndTimeShift: '1m'
            filterByTraceID: false
            filterBySpanID: false
            customQuery: true
            query: '{namespace="part1-demo"}'
          serviceMap:
            datasourceUid: victoriametrics
          nodeGraph:
            enabled: true
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: grafana
  namespace: monitoring
  labels:
    app: grafana
spec:
  replicas: 1
  selector:
    matchLabels:
      app: grafana
  template:
    metadata:
      labels:
        app: grafana
    spec:
      containers:
        - name: grafana
          image: grafana/grafana:11.5.2
          ports:
            - containerPort: 3000
              name: http
          env:
            - name: GF_AUTH_ANONYMOUS_ENABLED
              value: "true"
            - name: GF_AUTH_ANONYMOUS_ORG_ROLE
              value: "Admin"
            - name: GF_SECURITY_ALLOW_EMBEDDING
              value: "true"
          volumeMounts:
            - name: datasources
              mountPath: /etc/grafana/provisioning/datasources
      volumes:
        - name: datasources
          configMap:
            name: grafana-datasources
---
apiVersion: v1
kind: Service
metadata:
  name: grafana
  namespace: monitoring
  labels:
    app: grafana
spec:
  selector:
    app: grafana
  ports:
    - name: http
      port: 3000
      targetPort: 3000
'@ | Set-Content -Path "$HOME\mesh-study\part11-grafana.yaml" -Encoding UTF8

kubectl apply -f "$HOME\mesh-study\part11-grafana.yaml"
kubectl rollout status deployment/grafana -n monitoring
핵심 설정 해설

  • uid 고정 (victoriametrics, loki, tempo): Grafana가 랜덤 UID를 생성하도록 두면 데이터소스 간 점프 설정(datasourceUid: loki, datasourceUid: tempo)이 깨진다. 반드시 명시적인 uid를 지정해야 한다.
  • GF_AUTH_ANONYMOUS_ENABLED: "true" & GF_SECURITY_ALLOW_EMBEDDING: "true": 로컬 실습 편의성과 Kiali 대시보드 내 Grafana 차트 임베딩 연동을 위해 익명 Admin 접근 및 iframe 임베딩을 허용했다.
  • tracesToLogsV2: Tempo의 개별 Span에서 Logs for this span을 클릭했을 때 해당 Span의 시작/종료 시각(±1m)을 기준으로 {namespace="part1-demo"} 스트림의 Waypoint 및 애플리케이션 로그를 즉시 분할 창에 띄운다.

2.2 Grafana API를 통한 프로비저닝 검증

study-client 파드에서 Grafana의 /api/datasources REST API를 호출하여 3개 데이터소스와 상관분석 JSON 설정이 정확히 등록되었는지 확인한다.

kubectl exec -n part1-demo study-client -- curl -s http://grafana.monitoring.svc.cluster.local:3000/api/datasources

실제 응답 결과 (요약 정렬):

[
  {
    "id": 2,
    "uid": "loki",
    "name": "Loki",
    "type": "loki",
    "url": "http://loki.monitoring.svc.cluster.local:3100",
    "jsonData": {
      "derivedFields": [
        {
          "datasourceUid": "tempo",
          "matcherRegex": "\"([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\"",
          "name": "RequestID",
          "url": "{__value.raw}"
        }
      ]
    },
    "readOnly": true
  },
  {
    "id": 3,
    "uid": "tempo",
    "name": "Tempo",
    "type": "tempo",
    "url": "http://tempo.monitoring.svc.cluster.local:3200",
    "jsonData": {
      "nodeGraph": { "enabled": true },
      "serviceMap": { "datasourceUid": "victoriametrics" },
      "tracesToLogsV2": {
        "customQuery": true,
        "datasourceUid": "loki",
        "filterBySpanID": false,
        "filterByTraceID": false,
        "query": "{namespace=\"part1-demo\"}",
        "spanEndTimeShift": "1m",
        "spanStartTimeShift": "-1m"
      }
    },
    "readOnly": true
  },
  {
    "id": 1,
    "uid": "victoriametrics",
    "name": "VictoriaMetrics",
    "type": "prometheus",
    "url": "http://vmsingle.monitoring.svc.cluster.local:8428",
    "isDefault": true,
    "jsonData": {
      "exemplarTraceIdDestinations": [
        { "datasourceUid": "tempo", "name": "trace_id" }
      ],
      "httpMethod": "POST"
    },
    "readOnly": true
  }
]

3. Kiali Server (v2.33.0) 설치 및 LGTM 스택 연동

3.1 Helm을 이용한 Kiali Server 배포

Kiali가 기본적으로 찾는 http://prometheus.istio-system:9090 대신 우리가 구축한 monitoring 네임스페이스의 VictoriaMetrics (:8428), Grafana (:3000), **Tempo (:3200 / :4317)**를 바라보도록 external_services 파라미터를 주입하여 설치한다.

helm repo add kiali https://kiali.org/helm-charts
helm repo update

helm upgrade --install kiali-server kiali/kiali-server `
  --namespace istio-system `
  --set auth.strategy="anonymous" `
  --set external_services.prometheus.url="http://vmsingle.monitoring.svc.cluster.local:8428" `
  --set external_services.grafana.enabled=true `
  --set external_services.grafana.internal_url="http://grafana.monitoring.svc.cluster.local:3000" `
  --set external_services.tracing.enabled=true `
  --set external_services.tracing.provider="tempo" `
  --set external_services.tracing.internal_url="http://tempo.monitoring.svc.cluster.local:3200" `
  --set external_services.tracing.use_grpc=false

3.2 Kiali 연동 상태 (/kiali/api/status) 실측 검증

Kiali 파드가 1/1 Running 상태가 되면 /kiali/api/status 엔드포인트를 호출해 외부 LGTM 서비스들과의 연결 상태를 확인한다.

kubectl exec -n part1-demo study-client -- curl -s http://kiali.istio-system.svc.cluster.local:20001/kiali/api/status

실제 응답 결과:

{
  "status": {
    "Disabled features": "",
    "Kiali commit hash": "0b0d4e7e2a9eff0edf9815742794d6377ab26815",
    "Kiali container version": "v2.33.0",
    "Kiali state": "running",
    "Kiali version": "v2.33.0"
  },
  "externalServices": [
    {
      "name": "Kubernetes-Kubernetes",
      "version": "v1.31.1",
      "tempoConfig": {}
    },
    {
      "name": "Prometheus",
      "version": "2.24.0",
      "tempoConfig": {}
    },
    {
      "name": "Grafana",
      "version": "11.5.2",
      "tempoConfig": {}
    },
    {
      "name": "tempo",
      "version": "v2.7.1",
      "tempoConfig": {
        "cacheCapacity": 200,
        "cacheEnabled": true
      }
    }
  ],
  "warningMessages": [],
  "istioEnvironment": {
    "istioAPIEnabled": true
  }
}

4. Kiali 토폴로지 그래프 및 Ambient Mesh 설정 검증

4.1 트래픽 발생 및 실시간 토폴로지 그래프 (/kiali/api/namespaces/graph) 확인

study-client에서 order-api로 트래픽을 발생시킨 뒤, Kiali가 VictoriaMetrics의 istio_* 메트릭을 집계하여 생성한 실시간 토폴로지 그래프 데이터를 조회한다.

1..5 | ForEach-Object {
  kubectl exec -n part1-demo study-client -- curl -s -o /dev/null http://order-api.part1-demo.svc.cluster.local/api/orders
}
Start-Sleep -Seconds 6
kubectl exec -n part1-demo study-client -- curl -s "http://kiali.istio-system.svc.cluster.local:20001/kiali/api/namespaces/graph?namespaces=part1-demo&graphType=versionedApp&duration=10m"

실제 응답 중 핵심 노드 및 엣지(Edge) 발췌:

{
  "graphType": "versionedApp",
  "elements": {
    "nodes": [
      {
        "data": {
          "namespace": "part1-demo",
          "app": "order-api",
          "healthData": {
            "status": { "status": "Healthy", "totalRequestRate": 1.71 },
            "workloadStatuses": [
              { "name": "order-api", "availableReplicas": 2, "desiredReplicas": 2 },
              { "name": "order-api-v2", "availableReplicas": 1, "desiredReplicas": 1 }
            ]
          }
        }
      }
    ],
    "edges": [
      {
        "data": {
          "destPrincipal": "spiffe://cluster.local/ns/part1-demo/sa/default",
          "sourcePrincipal": "spiffe://cluster.local/ns/part1-demo/sa/default",
          "isMTLS": "100",
          "healthStatus": "Healthy",
          "traffic": {
            "protocol": "tcp",
            "responses": {
              "-": {
                "hosts": { "pg-vm.mesh.internal": "100.0" }
              }
            }
          }
        }
      },
      {
        "data": {
          "healthStatus": "Healthy",
          "isMTLS": "100",
          "traffic": {
            "protocol": "tcp",
            "responses": {
              "-": {
                "hosts": { "order-api.part1-demo.svc.cluster.local": "100.0" }
              }
            }
          },
          "waypoint": { "direction": "to" }
        }
      }
    ]
  }
}
토폴로지 JSON에서 확인할 수 있는 Ambient Mesh의 3가지 핵심 증거

  1. "isMTLS": "100" & "sourcePrincipal": "spiffe://cluster.local/ns/part1-demo/sa/default": 모든 파드 간 통신이 ztunnel에 의해 100% HBONE mTLS 암호화 및 SPIFFE ID 상호 인증으로 보호되고 있다.
  2. "waypoint": { "direction": "to" }: order-api 및 postgres-exporter로 향하는 트래픽이 L7 waypoint 프록시를 경유하고 있음을 Kiali가 정확히 인지한다.
  3. "hosts": { "pg-vm.mesh.internal": "100.0" }: 클러스터 외부 Docker 브릿지의 PostgreSQL 18 VM(172.19.0.7:5432)으로 나가는 TCP 이그레스 트래픽까지 Kiali 토폴로지 맵의 정식 엣지(Edge)로 시각화된다!

4.2 Kiali Istio CRD 정합성 검증 (/kiali/api/namespaces/part1-demo/istio)

Kiali가 part1-demo 네임스페이스의 Istio 및 Kubernetes Gateway API 리소스들을 어떻게 검증하고 있는지 조회한다.

kubectl exec -n part1-demo study-client -- curl -s "http://kiali.istio-system.svc.cluster.local:20001/kiali/api/namespaces/part1-demo/istio"

실제 응답 결과 (핵심 상태 요약):


5. 로컬 브라우저 UI 접속 가이드 (kubectl port-forward)

백그라운드 터미널 2개를 열어 Grafana와 Kiali 웹 UI를 로컬 PC 포트로 포워딩하면 브라우저에서 시각적으로 탐색할 수 있다.

# 1. Grafana UI 포트포워딩 (http://localhost:3000)
Start-Process powershell -ArgumentList "-NoExit", "-Command", "kubectl port-forward -n monitoring svc/grafana 3000:3000"

# 2. Kiali UI 포트포워딩 (http://localhost:20001)
Start-Process powershell -ArgumentList "-NoExit", "-Command", "kubectl port-forward -n istio-system svc/kiali 20001:20001"
  1. Grafana Explore (http://localhost:3000/explore):
    • 상단 데이터소스에서 Tempo 선택 → Search 탭에서 Service Name = order-api.part1-demo 조회
    • 검색된 Trace ID 클릭 → 우측 워터폴 뷰에서 Span 옆의 Logs for this span 아이콘 클릭
    • 화면이 좌우로 분할되며 우측 Loki 창에 해당 시각의 Waypoint Envoy Access Log가 나란히 출력되는 것을 확인!
  2. Kiali Console (http://localhost:20001/kiali):
    • 좌측 메뉴 Traffic Graph 클릭 → Namespace 드롭다운에서 part1-demo 체크
    • Display 옵션에서 Traffic Animation, Security (mTLS 자물쇠 아이콘), Waypoint Proxies 체크

6. 트러블슈팅 및 체크리스트

증상 원인 해결 방법
Tempo에서 Logs for this span 버튼이 비활성화됨 datasources.yaml의 tracesToLogsV2.datasourceUid가 실제 Loki의 uid와 불일치 Loki 데이터소스 정의에 명시적으로 uid: loki를 선언하고 tracesToLogsV2.datasourceUid: loki로 일치시킨다
Kiali Traffic Graph가 비어 있음 (Empty Graph) 최근 시간 창(Last 1m ~ 10m) 동안 메시 내부 트래픽이 없었거나 Prometheus URL 오타 study-client에서 curl 요청을 5~10회 발생시키고 Kiali 우측 상단 시간 범위를 Last 10m으로 조정한다
Kiali Istio Config에서 KIA1102 등 잘못된 VirtualService 경고 발생 동일한 hosts를 대상으로 중복된 라우팅 규칙이 충돌할 때 발생 kubectl get vs,httproute -n part1-demo로 중복 리소스를 확인하고 하나만 남긴다

7. 셀프 체크 Q&A


네비게이션

이전 목록 다음
◀ Part 11 이론 목록으로 Part 12 이론 ▶